package com.zjk.wrench.dynamic.config.center.domain.service;

import com.zjk.wrench.dynamic.config.center.config.DynamicConfigCenterAutoConfig;
import com.zjk.wrench.dynamic.config.center.config.DynamicConfigCenterAutoProperties;
import com.zjk.wrench.dynamic.config.center.domain.model.valobj.AttributeVO;
import com.zjk.wrench.dynamic.config.center.types.annotations.DCCValue;
import com.zjk.wrench.dynamic.config.center.types.annotations.DCCJsonValue;
import com.zjk.wrench.dynamic.config.center.types.common.Constants;
import com.zjk.wrench.dynamic.config.center.types.common.JsonConverter;
import org.apache.commons.lang.StringUtils;
import org.redisson.api.RBucket;
import org.redisson.api.RedissonClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.aop.framework.AopProxyUtils;
import org.springframework.aop.support.AopUtils;
import org.springframework.stereotype.Component;

import java.lang.reflect.Field;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;


public class DynamicConfigCenterService implements IDynamicConfigCenterService{

    private final Logger log = LoggerFactory.getLogger(DynamicConfigCenterService.class);
    private final DynamicConfigCenterAutoProperties properties;
    private final RedissonClient redissonClient;
    private final Map<String, Object> dccBeanGroup = new ConcurrentHashMap<>();
    private final JsonConverter jsonConverter;

    public DynamicConfigCenterService(DynamicConfigCenterAutoProperties properties,
                                     RedissonClient redissonClient,
                                     JsonConverter jsonConverter) {
        this.properties = properties;
        this.redissonClient = redissonClient;
        this.jsonConverter = jsonConverter;
    }

    @Override
    public Object proxyObject(Object bean) {
        Class<?> beanClass = bean.getClass();
        Object beanObject = bean;
        //拿到原始对象
        if (AopUtils.isAopProxy(beanObject)) {
            beanClass = AopUtils.getTargetClass(bean);
            beanObject = AopProxyUtils.getSingletonTarget(bean);
        }

        Field[] fields = beanClass.getDeclaredFields();
        for (Field field : fields) {
            if (field.isAnnotationPresent(DCCValue.class)) {
                DCCValue dccValue = field.getAnnotation(DCCValue.class);
                String value = dccValue.value();
                if (StringUtils.isBlank(value)) {
                    throw new RuntimeException(field.getName() + "@DCCValue config is blank");
                }
                String[] splits = value.split(Constants.SYMBOL_COLON);
                String key = properties.getKey(splits[0].trim());
                String defaultValue = splits.length == 2 ? splits[1] : null;
                String setValue = defaultValue;
                if (StringUtils.isBlank(defaultValue)) {
                    throw new RuntimeException("DCC config is not null " + field.getName() + "请配置默认值！");
                }
                RBucket<Object> bucket = redissonClient.getBucket(key);
                if (!bucket.isExists()) {
                    bucket.set(defaultValue);
                } else {
                    setValue = (String) bucket.get();
                }
                try {
                    field.setAccessible(true);
                    field.set(beanObject, setValue);
                    dccBeanGroup.put(beanClass.getName(), beanObject);
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
                dccBeanGroup.put(key,beanObject);
            } else if (field.isAnnotationPresent(DCCJsonValue.class)) {
                DCCJsonValue dccJsonValue = field.getAnnotation(DCCJsonValue.class);
                String value = dccJsonValue.value();
                if (StringUtils.isBlank(value)) {
                    throw new RuntimeException(field.getName() + "@DCCJsonValue config is blank");
                }
                String[] splits = value.split(Constants.SYMBOL_DOUBLE_COLON,2);
                if (splits.length < 2) {
                    throw new RuntimeException(field.getName() + "@DCCJsonValue format error, should be 'key::defaultJson'");
                }
                String key = properties.getKey(splits[0].trim());
                String defaultValue = splits.length == 2 ? splits[1] : null;
                String setValue = defaultValue;
                if (StringUtils.isBlank(defaultValue)) {
                    throw new RuntimeException("DCCJson config is not null " + field.getName() + "请配置默认值！");
                }
                RBucket<Object> bucket = redissonClient.getBucket(key);
                if (!bucket.isExists()) {
                    bucket.set(defaultValue);
                } else {
                    setValue = (String) bucket.get();
                }
                try {
                    field.setAccessible(true);
                    Object parsedValue = jsonConverter.fromJson(setValue, field.getType());
                    field.set(beanObject, parsedValue);
                    dccBeanGroup.put(beanClass.getName(), beanObject);
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
                dccBeanGroup.put(key, beanObject);
            }
        }
        return beanObject;
    }

    @Override
    public void adjust(AttributeVO attributeVO) {
        String key = properties.getKey(attributeVO.getAttribute());
        String value = attributeVO.getValue();
        RBucket<String> bucket = redissonClient.getBucket(key);
        boolean exists = bucket.isExists();
        if (!exists) return;
        bucket.set(attributeVO.getValue());
        Object objBean = dccBeanGroup.get(key);
        if (null == objBean) return;
        Class<?> objBeanClass = objBean.getClass();
        if (AopUtils.isAopProxy(objBean)) {
            objBeanClass = AopUtils.getTargetClass(objBean);
        }
        try {
            Field field = objBeanClass.getDeclaredField(attributeVO.getAttribute());
            field.setAccessible(true);
            Object newValue = attributeVO.getValue();
            if (field.isAnnotationPresent(DCCJsonValue.class)) {
                newValue = jsonConverter.fromJson((String) newValue, field.getType());
            }
            field.set(objBean, newValue);
            field.setAccessible(false);
            log.info("DCC 节点监听，动态设置值 {} {}", key, newValue);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}
